home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload Trio 2 / Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO / dir37 / ms_sh23s.zip / SRC / SH4.C < prev    next >
C/C++ Source or Header  |  1994-08-26  |  38KB  |  1,795 lines

  1. /*
  2.  * MS-DOS SHELL - 'word' Interpretator
  3.  *
  4.  * MS-DOS SHELL - Copyright (c) 1990,4 Data Logic Limited and Charles Forsyth
  5.  *
  6.  * This code is based on (in part) the shell program written by Charles
  7.  * Forsyth and the subsequence modifications made by Simon J. Gerraty (for
  8.  * his Public Domain Korn Shell) and is subject to the following copyright
  9.  * restrictions:
  10.  *
  11.  * 1.  Redistribution and use in source and binary forms are permitted
  12.  *     provided that the above copyright notice is duplicated in the
  13.  *     source form and the copyright notice in file sh6.c is displayed
  14.  *     on entry to the program.
  15.  *
  16.  * 2.  The sources (or parts thereof) or objects generated from the sources
  17.  *     (or parts of sources) cannot be sold under any circumstances.
  18.  *
  19.  *    $Header: /usr/users/istewart/shell/sh2.3/Release/RCS/sh4.c,v 2.13 1994/08/25 20:49:11 istewart Exp $
  20.  *
  21.  *    $Log: sh4.c,v $
  22.  *    Revision 2.13  1994/08/25  20:49:11  istewart
  23.  *    MS Shell 2.3 Release
  24.  *
  25.  *    Revision 2.12  1994/02/01  10:25:20  istewart
  26.  *    Release 2.3 Beta 2, including first NT port
  27.  *
  28.  *    Revision 2.11  1994/01/11  17:55:25  istewart
  29.  *    Release 2.3 Beta 0 patches
  30.  *
  31.  *    Revision 2.10  1993/08/25  16:03:57  istewart
  32.  *    Beta 225 - see Notes file
  33.  *
  34.  *    Revision 2.9  1993/07/02  10:21:35  istewart
  35.  *    224 Beta fixes
  36.  *
  37.  *    Revision 2.8  1993/06/14  11:00:12  istewart
  38.  *    More changes for 223 beta
  39.  *
  40.  *    Revision 2.7  1993/06/02  09:52:35  istewart
  41.  *    Beta 223 Updates - see Notes file
  42.  *
  43.  *    Revision 2.6  1993/02/16  16:03:15  istewart
  44.  *    Beta 2.22 Release
  45.  *
  46.  *    Revision 2.5  1993/01/26  18:35:09  istewart
  47.  *    Release 2.2 beta 0
  48.  *
  49.  *    Revision 2.4  1992/12/14  10:54:56  istewart
  50.  *    BETA 215 Fixes and 2.1 Release
  51.  *
  52.  *    Revision 2.3  1992/11/06  10:03:44  istewart
  53.  *    214 Beta test updates
  54.  *
  55.  *    Revision 2.2  1992/09/03  18:54:45  istewart
  56.  *    Beta 213 Updates
  57.  *
  58.  *    Revision 2.1  1992/07/10  10:52:48  istewart
  59.  *    211 Beta updates
  60.  *
  61.  *    Revision 2.0  1992/04/13  17:39:09  Ian_Stewartson
  62.  *    MS-Shell 2.0 Baseline release
  63.  *
  64.  */
  65.  
  66. #include <sys/types.h>
  67. #include <sys/stat.h>
  68. #include <stdio.h>
  69. #include <limits.h>            /* String library functions     */
  70. #include <signal.h>
  71. #include <errno.h>
  72. #include <setjmp.h>
  73. #include <dirent.h>
  74. #include <string.h>
  75. #include <stdlib.h>
  76. #include <unistd.h>
  77. #include <ctype.h>
  78. #include <fcntl.h>
  79. #include "sh.h"
  80.  
  81. /*
  82.  * string expansion
  83.  *
  84.  * first pass: quoting, IFS separation, ${} and $() substitution.
  85.  * second pass: filename expansion (*?[]~).
  86.  */
  87.  
  88. /*
  89.  * expansion generator state
  90.  */
  91.  
  92. typedef struct Expand {
  93.     /* int  type; */            /* see ExpandAWord()            */
  94.     char    *str;            /* string            */
  95.     union {
  96.     char    **strv;            /* string[]            */
  97.     FILE    *file;            /* file                */
  98.     }        u;            /* source            */
  99.  
  100.     bool    split;            /* split "$@"            */
  101. } Expand;
  102.  
  103. #define    XBASE    0        /* scanning original            */
  104. #define    XSUB    1        /* expanding ${} string            */
  105. #define    XARGSEP    2        /* ifs0 between "$@"            */
  106. #define    XARG    3        /* expanding $*, $@            */
  107. #define    XCOM    4        /* expanding $()            */
  108.  
  109. /*
  110.  * Quote processing
  111.  */
  112.  
  113. #define QUOTE_NONE    0    /* None                    */
  114. #define QUOTE_INSIDE    1    /* Inside quotes            */
  115. #define QUOTE_TEMP    2    /* Mark a temporary quote        */
  116.  
  117. /*
  118.  * for nested substitution: ${var:=$var2}
  119.  */
  120.  
  121. typedef struct SubType {
  122.     short    type;        /* [=+-?%#] action after expanded word    */
  123.     short    base;        /* begin position of expanded word    */
  124.     char    *name;        /* name for ${var=word}            */
  125.     int        index;        /* index ${var[index]=word}        */
  126. } SubType;
  127.  
  128. static void F_LOCAL    ExpandAWord (char *, Word_B **, int);
  129. static int F_LOCAL    VariableSubstitute (Expand *, char *, int, int);
  130. static int F_LOCAL    CommandSubstitute (Expand *, char *);
  131. static char * F_LOCAL    TrimSubstitute (SubType *, char *);
  132. static int F_LOCAL    MathsSubstitute (Expand *, char *);
  133. static void F_LOCAL    ExpandGlobCharacters (char *, Word_B **);
  134. static void F_LOCAL    GlobAWord (char *, char *, char *, Word_B **, bool);
  135. static char * F_LOCAL    RemoveMagicMarkers (unsigned char *);
  136. static char * F_LOCAL    TildeSubstitution (unsigned char *);
  137. static unsigned char * F_LOCAL    CheckForMultipleDrives (unsigned char *);
  138. static bool F_LOCAL    ProcessCommandTree (C_Op *, int);
  139. static char * F_LOCAL    WordScan (char *, int);
  140. static int F_LOCAL    HandleArrayValue (char *, char **, int);
  141. static void F_LOCAL    CheckForUnset (char *, int);
  142. static void F_LOCAL    AlternationExpand (char *, Word_B **, int);
  143. static int F_LOCAL    AlternationScan (char **, char **, char, int);
  144. static void        BuildVariableEntryList (const void *, VISIT, int);
  145. static int F_LOCAL     GetNumberofFloppyDrives (void);
  146.  
  147. static char        *PNullNSet = "%s: parameter null or not set";
  148. static char        *GVAV_Name;        /* Name for building a    */
  149.                         /* list of Variable    */
  150.                         /* Array values        */
  151. static Word_B        *GVAV_WordList;        /* Word block for list    */
  152.  
  153.  
  154. /*
  155.  * compile and expand word
  156.  */
  157.  
  158. char    *substitute (char *cp, int ExpandMode)
  159. {
  160.     struct source    *sold = source;
  161.     char        *res;
  162.  
  163.     source = pushs (SWSTR);
  164.     source->str = (char *) cp;
  165.  
  166.     if (ScanNextToken (ONEWORD) != PARSE_WORD)
  167.     PrintErrorMessage ("eval: substitute error");
  168.  
  169.     res = ExpandAString (yylval.cp, ExpandMode);
  170.     source = sold;
  171.     return res;
  172. }
  173.  
  174. /*
  175.  * expand arg-list
  176.  */
  177.  
  178. char    **ExpandWordList (char **ap, int ExpandMode, ExeMode *PMode)
  179. {
  180.     Word_B    *w = (Word_B *)NULL;
  181.     bool    FoundProgram = FALSE;
  182.     int        i;
  183.     int        InitialCount;
  184.     char    *CurrentAp;
  185.  
  186.     if ((ap == NOWORDS) || (*ap == NOWORD))
  187.     return ap;
  188.  
  189. /* Expand the arguments */
  190.  
  191.     while (*ap != NOWORD)
  192.     {
  193.     InitialCount = WordBlockSize (w);
  194.     ExpandAWord (CurrentAp = *(ap++), &w, ExpandMode);
  195.  
  196. /*
  197.  * Get the program mode for expansion of words and globs.  Update the
  198.  * current mode to reflect it.
  199.  */
  200.  
  201.     if ((!FoundProgram) && (PMode != (ExeMode *)NULL) && WordBlockSize (w))
  202.     {
  203.         CheckProgramMode (w->w_words[0], PMode);
  204.  
  205.         if (PMode->Flags & EP_NOEXPAND)
  206.         ExpandMode &= ~EXPAND_GLOBBING;
  207.  
  208.         if (PMode->Flags & EP_NOWORDS)
  209.         ExpandMode &= ~EXPAND_SPLITIFS;
  210.  
  211.         if (PMode->Flags & EP_CONVERT)
  212.         ExpandMode |= EXPAND_CONVERT;
  213.     }
  214.  
  215. /* Convert UNIX directories to DOS and - to / ?, except for first
  216.  * argument
  217.  */
  218.  
  219.     if (ExpandMode & EXPAND_CONVERT)
  220.     {
  221.         for (i = max (InitialCount, 1); i < WordBlockSize (w); i++)
  222.         {
  223.         PATH_TO_DOS (w->w_words[i]);
  224.  
  225. /* Convert - to /, if the string is not quoted */
  226.  
  227.         if ((*(w->w_words[i]) == CHAR_SWITCH) &&
  228.             ((*CurrentAp != WORD_OQUOTE) ||
  229.              (*CurrentAp != WORD_QTCHAR)))
  230.             *(w->w_words[i]) = '/';
  231.         }
  232.     }
  233.     }
  234.  
  235. /* Return the word list */
  236.  
  237.     return GetWordList (AddWordToBlock (NOWORD, w));
  238. }
  239.  
  240. /*
  241.  * expand string
  242.  */
  243.  
  244. char    *ExpandAString (char *cp, int ExpandMode)
  245. {
  246.     Word_B        *w = (Word_B *)NULL;
  247.  
  248.     ExpandAWord (cp, &w, ExpandMode);
  249.  
  250.     return (WordBlockSize (w) == 0) ? null : w->w_words[0];
  251. }
  252.  
  253. /*
  254.  * expand string - return only one component
  255.  * used from iosetup to expand redirection files
  256.  */
  257.  
  258. char    *ExpandOneStringFirstComponent (char *cp, int ExpandMode)
  259. {
  260.     Word_B        *w = (Word_B *)NULL;
  261.  
  262.     ExpandAWord (cp, &w, ExpandMode);
  263.  
  264.     switch (WordBlockSize (w))
  265.     {
  266.     case 0:
  267.         return null;
  268.  
  269.     case 1:
  270.         return w->w_words[0];
  271.     }
  272.  
  273.     return ExpandAString (cp, ExpandMode & ~EXPAND_GLOBBING);
  274. }
  275.  
  276. /*
  277.  * Expand a word or two!
  278.  */
  279.  
  280. static void F_LOCAL ExpandAWord (char   *OriginalWord,    /* input word    */
  281.                  Word_B **WordList,  /* output word list*/
  282.                  int    ExpandMode)  /* Expand Flags    */
  283. {
  284.     int            c;
  285.     int            type = XBASE;    /* expansion type        */
  286.     int            QuoteStatus = 0;    /* quoted        */
  287.     int            quotestack[11];    /* Keep this bigger than the    */
  288.                     /* subtype stack        */
  289.     int            *qst = quotestack + 11;
  290.     XString        ds;        /* Expandable destination string*/
  291.     unsigned char    *dp;        /* Pointer into Destination S.    */
  292.     char        *sp;        /* source            */
  293.     int            fdo;        /* second pass flags; have word */
  294.     int            word;
  295.     int            combase;
  296.     int            ArrayIndex;
  297.     Expand        x;        /* expansion variables */
  298.     SubType        subtype [10];    /* substitution type stack */
  299.     SubType        *st = subtype + 10;
  300.     int            newlines;    /* For trailing newlines in COMSUB */
  301.     int            trimming = 0;    /* flag if expanding ${var#pat}    */
  302.                     /* or ${var%pat}        */
  303.     char        ifs0 = *GetVariableAsString (IFS, FALSE);
  304.  
  305.     if (OriginalWord == NULL)
  306.     PrintErrorMessage ("eval: expanding a NULL");
  307.  
  308.     if (FL_TEST (FLAG_DISABLE_GLOB))
  309.     ExpandMode &= (~EXPAND_GLOBBING);
  310.  
  311. /*
  312.  * Look for '{' in the input word
  313.  */
  314.  
  315.     if ((ShellGlobalFlags & FLAGS_ALTERNATION) &&
  316.     (!(ExpandMode & EXPAND_NOALTS)) &&
  317.     (ExpandMode & EXPAND_GLOBBING) &&
  318.     ((sp = strchr (OriginalWord, CHAR_OPEN_BRACES)) != (char *)NULL) &&
  319.     (sp[-1] == WORD_CHAR) &&
  320.     (!(sp[1] == WORD_CHAR && sp[2] == CHAR_CLOSE_BRACES)))
  321.     {
  322.     AlternationExpand (OriginalWord, WordList, ExpandMode);
  323.     return;
  324.     }
  325.  
  326.     ExpandMode &= ~EXPAND_NOALTS;
  327.  
  328. /* Initialise */
  329.  
  330.     dp = (unsigned char *)XCreate (&ds, 128);    /* destination string     */
  331.     type = XBASE;
  332.     sp = OriginalWord;
  333.     fdo = 0;
  334.     word = !(ExpandMode & EXPAND_SPLITIFS);
  335.  
  336. /* The Main loop!! */
  337.  
  338.     while (1)
  339.     {
  340.     XCheck (&ds, (unsigned char **)(&dp));
  341.  
  342.     switch (type)
  343.     {
  344.         case XBASE:            /* original prefixed string    */
  345.  
  346.         switch ((c = *(sp++)))
  347.         {
  348.             case WORD_EOS:        /* End - Hurray        */
  349.             c = 0;
  350.             break;
  351.  
  352.             case WORD_CHAR:        /* Normal char        */
  353.             c = *(sp++);
  354.             break;
  355.  
  356.             case WORD_QCHAR:        /* Escaped char        */
  357.             case WORD_QTCHAR:
  358.             QuoteStatus |= QUOTE_TEMP;/* temporary quote    */
  359.             c = *(sp++);
  360.             break;
  361.  
  362.             case WORD_OQUOTE:        /* Start quoted        */
  363.             case WORD_ODQUOTE:
  364.             word = 1;
  365.             QuoteStatus = QUOTE_INSIDE;
  366.             continue;
  367.  
  368.             case WORD_CQUOTE:        /* End quoted        */
  369.             case WORD_CDQUOTE:
  370.             QuoteStatus = QUOTE_NONE;
  371.             continue;
  372.  
  373.             case WORD_COMSUB:        /* $(....)        */
  374.             type = CommandSubstitute (&x, sp);
  375.             sp = strchr (sp, 0) + 1;
  376.             combase = XCurrentOffset (ds, dp);
  377.             newlines = 0;
  378.             continue;
  379.  
  380.             case WORD_OMATHS:        /* $((....))        */
  381.             type = MathsSubstitute (&x, sp);
  382.             sp = strchr (sp, 0) + 1;
  383.             continue;
  384.  
  385.             case WORD_OSUBST:    /* ${var{:}[=+-?]word}        */
  386.             OriginalWord = sp;         /* variable    */
  387.             sp = strchr (sp, 0) + 1;    /* skip variable */
  388.  
  389. /* Check for Array Variable */
  390.  
  391.             ArrayIndex = 0;
  392.             if (*sp == WORD_OARRAY)
  393.                 ArrayIndex = HandleArrayValue (OriginalWord, &sp,
  394.                                ExpandMode);
  395.  
  396.             c = (*sp == WORD_CSUBST) ? 0 : *(sp++);
  397.  
  398. /* Check for match option */
  399.  
  400.             if (((c & 0x7f) == CHAR_MATCH_START) ||
  401.                 ((c & 0x7f) == CHAR_MATCH_END))
  402.             {
  403.                 CheckForUnset (OriginalWord, ArrayIndex);
  404.                 trimming++;
  405.                 type = XBASE;
  406.                 *--qst = QuoteStatus;
  407.                 QuoteStatus = QUOTE_NONE;
  408.             }
  409.  
  410.             else
  411.                 type = VariableSubstitute (&x, OriginalWord,
  412.                                c, ArrayIndex);
  413.  
  414. /* expand? */
  415.  
  416.             if (type == XBASE)
  417.             {
  418.                 if (st == subtype)
  419.                 ShellErrorMessage ("ridiculous ${} nesting");
  420.  
  421.                 --st;
  422.                 st->type = c;
  423.                 st->base = XCurrentOffset (ds, dp);
  424.                 st->name = OriginalWord;
  425.                 st->index = ArrayIndex;
  426.             }
  427.  
  428.             else
  429.                 sp = WordScan (sp, WORD_CSUBST); /* skip word */
  430.  
  431.             continue;
  432.  
  433.             case WORD_CSUBST: /* only get here if expanding word */
  434.             *dp = 0;
  435.  
  436.             if (ExpandMode & EXPAND_GLOBBING)
  437.                 ExpandMode &= (~EXPAND_PATTERN);
  438.  
  439. /*
  440.  *  Check that full functionality is here!
  441.  */
  442.             switch (st->type & 0x7f)
  443.             {
  444.                 case CHAR_MATCH_START:
  445.                 case CHAR_MATCH_END:
  446.                 *dp = 0;
  447.                 dp = XResetOffset (ds, st->base);
  448.  
  449.                 QuoteStatus = *(qst++);
  450.                 x.str = TrimSubstitute (st, (char *)dp);
  451.                 type = XSUB;
  452.                 trimming--;
  453.                 continue;
  454.  
  455.                 case CHAR_ASSIGN:
  456.                 SetVariableArrayFromString
  457.                     (st->name, st->index,
  458.                      (char *)XResetOffset (ds, st->base));
  459.                 break;
  460.  
  461.                 case '?':
  462.                 if (dp == XResetOffset (ds, st->base))
  463.                     ShellErrorMessage (PNullNSet, OriginalWord);
  464.  
  465.                 else
  466.                     ShellErrorMessage ("%s",
  467.                                XResetOffset (ds,
  468.                                      st->base));
  469.             }
  470.  
  471.             st++;
  472.             type = XBASE;
  473.             continue;
  474.         }
  475.  
  476.         break;
  477.  
  478.         case XSUB:
  479.         if ((c = *(x.str++)) == 0)
  480.         {
  481.             type = XBASE;
  482.             continue;
  483.         }
  484.  
  485.         break;
  486.  
  487.         case XARGSEP:
  488.         type = XARG;
  489.         QuoteStatus = QUOTE_INSIDE;
  490.  
  491.         case XARG:
  492.         if ((c = *(x.str++)) == 0)
  493.         {
  494.             if ((x.str = *(x.u.strv++)) == NULL)
  495.             {
  496.             type = XBASE;
  497.             continue;
  498.             }
  499.  
  500.             else if (QuoteStatus && x.split)
  501.             {
  502.             type = XARGSEP;        /* terminate word for "$@" */
  503.             QuoteStatus = QUOTE_NONE;
  504.             }
  505.  
  506.             c = ifs0;
  507.         }
  508.  
  509.         break;
  510.  
  511.         case XCOM:
  512.         if (newlines)            /* Spit out saved nl's    */
  513.         {
  514.             c = CHAR_NEW_LINE;
  515.             --newlines;
  516.         }
  517.  
  518.         else
  519.         {
  520.             while ((c = getc (x.u.file)) == CHAR_NEW_LINE)
  521.             newlines++;        /* Save newlines    */
  522.  
  523.             if (newlines && (c != EOF))
  524.             {
  525.             ungetc (c, x.u.file);
  526.             c = CHAR_NEW_LINE;
  527.             --newlines;
  528.             }
  529.         }
  530.  
  531.         if (c == EOF)
  532.         {
  533.             OriginalWord = (char *)XResetOffset (ds, combase);
  534.             newlines = 0;
  535.             S_fclose (x.u.file, TRUE);
  536.             type = XBASE;
  537.             continue;
  538.         }
  539.  
  540.         break;
  541.     }
  542.  
  543. /* check for end of word or IFS separation */
  544.  
  545.     if ((c == 0) || (!QuoteStatus && (ExpandMode & EXPAND_SPLITIFS) &&
  546.              IS_IFS (c)))
  547.     {
  548.         if (word)
  549.         {
  550.         *(dp++) = 0;
  551.         OriginalWord = XClose (&ds, dp);
  552.  
  553.         if (fdo & EXPAND_TILDE)
  554.             OriginalWord =
  555.             TildeSubstitution ((unsigned char *)OriginalWord);
  556.  
  557.         if (fdo & EXPAND_GLOBBING)
  558.             ExpandGlobCharacters (OriginalWord, WordList);
  559.  
  560.         else
  561.             *WordList = AddWordToBlock (OriginalWord, *WordList);
  562.  
  563. /* Re-set */
  564.         fdo = 0;
  565.         word = 0;
  566.  
  567.         if (c != 0)
  568.             dp = (unsigned char *)XCreate (&ds, 128);
  569.         }
  570.  
  571.         if (c == 0)
  572.         return;
  573.     }
  574.  
  575. /*
  576.  * Mark any special second pass chars
  577.  */
  578.     else
  579.     {
  580.         if (!QuoteStatus)
  581.         {
  582.         switch (c)
  583.         {
  584.             case CHAR_MATCH_ALL:
  585.             case CHAR_MATCH_ANY:
  586.             case CHAR_OPEN_BRACKETS:
  587.             if ((ExpandMode & (EXPAND_PATTERN | EXPAND_GLOBBING)) ||
  588.                 trimming)
  589.             {
  590.                 fdo |= (ExpandMode & EXPAND_GLOBBING);
  591.                 *dp++ = CHAR_MAGIC;
  592.             }
  593.  
  594.             break;
  595.  
  596. /*
  597.  * Check for [^...
  598.  */
  599.  
  600.             case CHAR_NOT:
  601.             if (((ExpandMode & (EXPAND_PATTERN | EXPAND_GLOBBING))
  602.                 || trimming) &&
  603.                 ((dp[-1] == CHAR_OPEN_BRACKETS) &&
  604.                  (dp[-2] == CHAR_MAGIC)))
  605.                 *dp++ = CHAR_MAGIC;
  606.             break;
  607.  
  608.             case CHAR_TILDE:
  609.             if (((ExpandMode & EXPAND_TILDE) &&
  610.                  (dp == XStart (ds))) ||
  611.                 (!(ExpandMode & EXPAND_SPLITIFS) &&
  612.                  (dp[-1] == '=' || dp[-1] == ':')))
  613.             {
  614.                 fdo |= EXPAND_TILDE;
  615.                 *dp++ = CHAR_MAGIC;
  616.             }
  617.  
  618.             break;
  619.         }
  620.         }
  621.  
  622.         else
  623.         QuoteStatus &= ~QUOTE_TEMP;    /* undo temporary    */
  624.  
  625.         word = 1;
  626.         *dp++ = (char)c;        /* save output char    */
  627.     }
  628.     }
  629. }
  630.  
  631. /*
  632.  * Prepare to generate the string returned by ${} substitution.
  633.  */
  634.  
  635. static int F_LOCAL VariableSubstitute (Expand    *xp,
  636.                        char    *name,
  637.                        int    stype,
  638.                        int    Index)
  639. {
  640.     int        c;
  641.     int        type;
  642.  
  643. /* Handle ${#*|@}
  644.  *        ${#name[*]}
  645.  *        ${#name[value]}
  646.  *
  647.  * String length or argc
  648.  */
  649.  
  650.     if ((*name == '#') && ((c = name[1]) != 0))
  651.     {
  652.     if ((c == '*') || (c == '@'))
  653.         c = ParameterCount - 1;
  654.  
  655.     else if (Index < 0)
  656.         c = CountVariableArraySize (name + 1);
  657.  
  658.     else
  659.         c = strlen (GetVariableArrayAsString (name + 1, Index, FALSE));
  660.  
  661.     xp->str = StringCopy (IntegerToString (c));
  662.     return XSUB;
  663.     }
  664.  
  665.     c = *name;
  666.  
  667. /* Handle ${*|@}
  668.  *        ${*|@[*|@]}
  669.  *
  670.  * Use Parameter list
  671.  */
  672.  
  673.     if (c == '*' || c == '@')
  674.     {
  675.     if (ParameterCount == 0)
  676.     {
  677.         xp->str = null;
  678.         type = XSUB;
  679.     }
  680.  
  681.     else
  682.     {
  683.         xp->u.strv = ParameterArray + 1 + ((Index >= 0) ? Index : 0);
  684.         xp->str = *(xp->u.strv++);
  685.         xp->split = C2bool (c == '@');        /* $@ */
  686.         type = XARG;
  687.     }
  688.     }
  689.  
  690. /* ${name[*|@]} */
  691.  
  692.     else if (Index < 0)
  693.     {
  694.  
  695. /* Build list of values */
  696.  
  697.     if (isdigit (*name))
  698.     {
  699.         for (c = 0; isdigit (*name) && (c < 1000); name++)
  700.         c = c * 10 + *name - '0';
  701.  
  702.         xp->u.strv = (c <= ParameterCount) ? ParameterArray + c
  703.                            : NOWORDS;
  704.     }
  705.  
  706.     else
  707.     {
  708.         GVAV_Name = name;
  709.         GVAV_WordList = (Word_B *)NULL;
  710.         twalk (VariableTree, BuildVariableEntryList);
  711.         xp->u.strv = WordBlockSize (GVAV_WordList)
  712.                 ? GetWordList (AddWordToBlock (NOWORD,
  713.                                    GVAV_WordList))
  714.                 : NOWORDS;
  715.     }
  716.  
  717. /* Set up list.  Check to see if there any any entries */
  718.  
  719.     if (xp->u.strv == NOWORDS)
  720.     {
  721.         xp->str = null;
  722.         type = XSUB;
  723.     }
  724.  
  725.     else
  726.     {
  727.         xp->str = *(xp->u.strv++);
  728.         xp->split = C2bool (Index == -2);         /* ${name[@]} */
  729.         type = XARG;
  730.     }
  731.     }
  732.  
  733. /* ${name[num]} */
  734.  
  735.     else
  736.     {
  737.     xp->str = GetVariableArrayAsString (name, Index, TRUE);
  738.     type = XSUB;
  739.     }
  740.  
  741.     c = stype & 0x7F;
  742.  
  743. /* test the compiler's code generator */
  744.  
  745.     if ((c == CHAR_MATCH_END) || (c == CHAR_MATCH_START) ||
  746.     (((stype & CHAR_MAGIC) ? (*xp->str == 0)
  747.                    : (xp->str == null))
  748.         ? (c == '=') || (c == '-') || (c == '?')
  749.         : (c == '+')))
  750.     type = XBASE;    /* expand word instead of variable value */
  751.  
  752. /* Check for unset value */
  753.  
  754.     if ((type != XBASE) && FL_TEST (FLAG_UNSET_ERROR) &&
  755.         (xp->str == null) && (c != '+'))
  756.     ShellErrorMessage ("unset variable %s", name);
  757.  
  758.     return type;
  759. }
  760.  
  761. /*
  762.  * Run the command in $(...) and read its output.
  763.  */
  764.  
  765. static int F_LOCAL CommandSubstitute (Expand *xp, char *cp)
  766. {
  767.     Source    *s;
  768.     C_Op    *t;
  769.     FILE    *fi;
  770.     jmp_buf    ReturnPoint;
  771.     int        localpipe;
  772.  
  773.     if ((localpipe = OpenAPipe ()) < 0)
  774.     return XBASE;
  775.  
  776. /* Create a new environment */
  777.  
  778.     CreateNewEnvironment ();
  779.     MemoryAreaLevel++;
  780.  
  781.     if (SetErrorPoint (ReturnPoint))
  782.     {
  783.         QuitCurrentEnvironment ();
  784.         ReleaseMemoryArea (MemoryAreaLevel--);    /* free old space */
  785.     ClearExtendedLineFile ();
  786.     S_close (localpipe, TRUE);
  787.     return XBASE;
  788.     }
  789.  
  790. /* Create line buffer */
  791.  
  792.     e.line = GetAllocatedSpace (LINE_MAX);
  793.  
  794. /* Parse the command */
  795.  
  796.     s = pushs (SSTRING);
  797.     s->str = cp;
  798.  
  799. /* Check for $(<file) */
  800.  
  801.     if (((t = BuildParseTree (s)) != (C_Op *)NULL) &&
  802.     (t->type == TCOM)     && (*t->args == NOWORD) &&
  803.     (*t->vars == NOWORD)  && (t->ioact != (IO_Actions **)NULL))
  804.     {
  805.     IO_Actions    *io = *t->ioact;
  806.     char        *name;
  807.  
  808. /* We don't need the pipe - so get rid of it */
  809.  
  810.     S_close (localpipe, TRUE);
  811.  
  812. /* OK - terminate the created environment */
  813.  
  814.     QuitCurrentEnvironment ();
  815.  
  816.     if ((io->io_flag & IOTYPE) != IOREAD)
  817.         ShellErrorMessage ("funny $() command");
  818.  
  819.     if ((localpipe = S_open (FALSE, name = ExpandAString (io->io_name,
  820.                                   EXPAND_TILDE),
  821.                  O_RMASK)) < 0)
  822.         ShellErrorMessage ("cannot open %s", name);
  823.     }
  824.  
  825. /* Execute the command */
  826.  
  827.     else
  828.     {
  829.     if (!ProcessCommandTree (t, localpipe))
  830.         longjmp (ReturnPoint, 1);
  831.  
  832.     QuitCurrentEnvironment ();
  833.     }
  834.  
  835. /* Open the IO Stream */
  836.  
  837.     if ((fi = ReOpenFile (ReMapIOHandler (localpipe),
  838.                   sOpenReadMode)) == (FILE *)NULL)
  839.     ShellErrorMessage ("cannot open $() input");
  840.  
  841. /*
  842.  * Free old memory area
  843.  */
  844.  
  845.     ReleaseMemoryArea (MemoryAreaLevel--);
  846.     xp->u.file = fi;
  847.     return XCOM;
  848. }
  849.  
  850. /*
  851.  * perform #pattern and %pattern substitution in ${}
  852.  */
  853.  
  854. static char * F_LOCAL TrimSubstitute (SubType *st, char *pat)
  855. {
  856.     int            mode = GM_SHORTEST;
  857.     char        *pos;
  858.     char        *tsp;
  859.     char        *str = GetVariableArrayAsString (st->name, st->index,
  860.                              TRUE);
  861.  
  862. /*
  863.  * Switch on the match type
  864.  */
  865.  
  866.     switch (st->type & 0xff)
  867.     {
  868.     case CHAR_MATCH_START | CHAR_MAGIC:/* longest match at begin    */
  869.         mode = GM_LONGEST;
  870.  
  871.     case CHAR_MATCH_START:        /* shortest at begin        */
  872.         if (GeneralPatternMatch (str, (unsigned char *)pat, FALSE, &pos, mode))
  873.         return pos;
  874.  
  875.         break;
  876.  
  877.     case CHAR_MATCH_END | CHAR_MAGIC:/* longest match at end    */
  878.         mode = GM_LONGEST;
  879.  
  880.     case CHAR_MATCH_END:        /* shortest match at end    */
  881.         if (SuffixPatternMatch (str, pat, &pos, mode))
  882.         {
  883.         tsp = StringCopy (str);
  884.         tsp[pos - str] = 0;
  885.         return tsp;
  886.         }
  887.  
  888.         break;
  889.  
  890.     }
  891.  
  892.     return str;        /* no match, return string */
  893. }
  894.  
  895. /*
  896.  * glob
  897.  * Name derived from V6's /etc/glob, the program that expanded filenames.
  898.  */
  899.  
  900. static void F_LOCAL ExpandGlobCharacters (char *Pattern, Word_B **WordList)
  901. {
  902.     char        path [FFNAME_MAX];
  903.     int            oldsize = WordBlockSize (*WordList);
  904.     int            newsize;
  905.  
  906. #if (OS_TYPE != OS_UNIX)
  907.     char        *NewPattern;        /* Search file name    */
  908.     int            CurrentDrive;        /* Current drive    */
  909.     int            MaxDrives;        /* Max drive        */
  910.     int            SelectedDrive;        /* Selected drive    */
  911.     int            y_drive;        /* Dummies        */
  912.     unsigned char    *DriveCharacter;    /* Multi-drive flag    */
  913.     char        SDriveString[2];
  914.     char        *EndPattern;
  915.  
  916. /* Search all drives ? */
  917.  
  918.     if ((DriveCharacter = CheckForMultipleDrives (Pattern))
  919.                 != (unsigned char *)NULL)
  920.     {
  921.     CurrentDrive = GetCurrentDrive ();
  922.     MaxDrives = SetCurrentDrive (CurrentDrive);
  923.     SDriveString[1] = 0;
  924.     EndPattern = WordScan (Pattern, 0);
  925.     NewPattern = GetAllocatedSpace ((EndPattern - Pattern) + 1);
  926.  
  927. /* Scan the available drives */
  928.  
  929.     for (SelectedDrive = 1; SelectedDrive <= MaxDrives; ++SelectedDrive)
  930.     {
  931.         if (SetCurrentDrive (SelectedDrive) != -1)
  932.         {
  933.         y_drive = GetCurrentDrive ();
  934.         SetCurrentDrive (CurrentDrive);
  935.         }
  936.  
  937.         else
  938.             y_drive = -1;
  939.  
  940. /* Check to see if the second diskette drive is really there */
  941.  
  942.         if ((GetNumberofFloppyDrives () < 2) && (SelectedDrive == 2))
  943.         continue;
  944.  
  945. /* If the drive exists and is in our list - process it */
  946.  
  947.         *DriveCharacter = 0;
  948.         *SDriveString = GetDriveLetter (SelectedDrive);
  949.         strlwr (Pattern);
  950.  
  951.         if ((y_drive == SelectedDrive) &&
  952.         GeneralPatternMatch (SDriveString, Pattern, TRUE, (char **)NULL,
  953.                      GM_ALL))
  954.         {
  955.         *DriveCharacter = CHAR_DRIVE;
  956.         *NewPattern = *SDriveString;
  957.         memcpy (NewPattern + 1, DriveCharacter,
  958.             ((unsigned char *)EndPattern - DriveCharacter) + 1);
  959.  
  960.         GlobAWord (path, path, NewPattern, WordList, FALSE);
  961.         }
  962.  
  963.         *DriveCharacter = CHAR_DRIVE;
  964.     }
  965.  
  966.     ReleaseMemoryCell (NewPattern);
  967.     }
  968.  
  969. /*
  970.  * No special processing for drives
  971.  */
  972.  
  973.     else
  974.     GlobAWord (path, path, Pattern, WordList, FALSE);
  975. #else
  976.  
  977. /* UNIX has not drives. Goodie! */
  978.  
  979.     GlobAWord (path, path, Pattern, WordList, FALSE);
  980. #endif
  981.  
  982. /*
  983.  * Sort or something
  984.  */
  985.  
  986.     if ((newsize = WordBlockSize (*WordList)) == oldsize)
  987.     *WordList = AddWordToBlock (RemoveMagicMarkers ((unsigned char *)Pattern),
  988.                     *WordList);
  989.  
  990.     else
  991.     qsort (&(*WordList)->w_words[oldsize], (size_t)(newsize - oldsize),
  992.            sizeof (char *), SortCompare);
  993. }
  994.  
  995. /*
  996.  * Recursive bit
  997.  */
  998.  
  999. static void F_LOCAL GlobAWord (char   *ds,    /* dest path        */
  1000.                    char   *dp,    /* dest end        */
  1001.                          char   *sp,    /* source path        */
  1002.                    Word_B **WordList,/* output list        */
  1003.                    bool   check)    /* check dest existence */
  1004. {
  1005.     char        *EndFileName;        /* next source component */
  1006.     char        EndChar;
  1007.     char        *CFileName;
  1008.     char        *tdp;
  1009.     DIR            *dirp;
  1010.     struct dirent    *d;
  1011.     bool        IgnoreCase = TRUE;
  1012.  
  1013. /* End of source path ? */
  1014.  
  1015.     if (sp == (char *)NULL)
  1016.     {
  1017.     if (check && (!S_access (ds, F_OK)))
  1018.         return;
  1019.  
  1020.     *WordList = AddWordToBlock (StringCopy (ds), *WordList);
  1021.     return;
  1022.     }
  1023.  
  1024.     if ((dp > ds) && (!IsDriveCharacter (*(dp - 1))))
  1025.     *dp++ = CHAR_UNIX_DIRECTORY;
  1026.  
  1027.     while (IsPathCharacter (*sp))
  1028.     *(dp++) = *(sp++);
  1029.  
  1030. /* Find end of current file name */
  1031.  
  1032.     if ((EndFileName = FindPathCharacter (sp)) != (char *)NULL)
  1033.     {
  1034.     *(EndFileName++) = 0;
  1035.     EndChar = CHAR_UNIX_DIRECTORY;
  1036.     }
  1037.  
  1038. #if (OS_TYPE != OS_UNIX)
  1039.     if ((tdp = strchr (sp, CHAR_DRIVE)) != (char *)NULL)
  1040.     {
  1041.     if (EndFileName != (char *)NULL)
  1042.         *(--EndFileName) = CHAR_UNIX_DIRECTORY;
  1043.  
  1044.     EndFileName = tdp;
  1045.     *(EndFileName++) = 0;
  1046.     EndChar = CHAR_DRIVE;
  1047.     }
  1048. #endif
  1049.  
  1050.     *dp = 0;
  1051.  
  1052.  /* contains no pattern? */
  1053.  
  1054.     if (strchr (sp, CHAR_MAGIC) == NULL)
  1055.     {
  1056.     tdp = dp;
  1057.     CFileName = sp;
  1058.  
  1059.     while ((*(tdp++) = *(CFileName++)) != 0)
  1060.         continue;
  1061.  
  1062.     if (IsDriveCharacter (EndChar))
  1063.     {
  1064.         *(tdp - 1) = CHAR_DRIVE;
  1065.         *tdp = 0;
  1066.     }
  1067.  
  1068.     else
  1069.         --tdp;
  1070.  
  1071.     GlobAWord (ds, tdp, EndFileName, WordList, check);
  1072.     }
  1073.  
  1074.     else
  1075.     {
  1076.  
  1077. /* Check for drive letter and append a . to get the current directory */
  1078.  
  1079.     if ((strlen (ds) == 2) && IsDriveCharacter (*(ds + 1)))
  1080.     {
  1081.         *(ds + 2) = CHAR_PERIOD;
  1082.         *(ds + 3) = 0;
  1083.     }
  1084.  
  1085. /* Scan the directory */
  1086.  
  1087.     if ((dirp = opendir ((*ds == 0) ? CurrentDirLiteral
  1088.                     : ds)) != (DIR *)NULL)
  1089.     {
  1090.         if ((IsHPFSFileSystem ((*ds == 0) ? CurrentDirLiteral : ds)) &&
  1091.         (!(ShellGlobalFlags & FLAGS_NOCASE)))
  1092.         IgnoreCase = FALSE;
  1093.  
  1094.         while ((d = readdir (dirp)) != (struct dirent *)NULL)
  1095.         {
  1096.         CFileName = d->d_name;
  1097.  
  1098. /*
  1099.  * Ignore . * ..
  1100.  */
  1101.  
  1102.         if ((*CFileName == CHAR_PERIOD) &&
  1103.             ((*(CFileName + 1) == 0) ||
  1104.              ((*(CFileName + 1) == CHAR_PERIOD) &&
  1105.               (*(CFileName + 2) == 0))))
  1106.             continue;
  1107.  
  1108. /*
  1109.  * Ignore . files unless match starts with a dot.
  1110.  */
  1111.  
  1112.         if ((*CFileName == CHAR_PERIOD && *sp != CHAR_PERIOD) ||
  1113.             !GeneralPatternMatch (CFileName, (unsigned char *)sp,
  1114.                       IgnoreCase, (char **)NULL, GM_ALL))
  1115.             continue;
  1116.  
  1117.         tdp = dp;
  1118.         while ((*tdp++ = *CFileName++) != 0)
  1119.             continue;
  1120.  
  1121.         --tdp;
  1122.  
  1123.         GlobAWord (ds, tdp, EndFileName, WordList,
  1124.                C2bool (EndFileName != NULL));
  1125.         }
  1126.  
  1127.         closedir (dirp);
  1128.     }
  1129.     }
  1130.  
  1131.     if (EndFileName != NULL)
  1132.     *(--EndFileName) = EndChar;
  1133. }
  1134.  
  1135. /*
  1136.  * remove MAGIC from string
  1137.  */
  1138.  
  1139. static char * F_LOCAL RemoveMagicMarkers (unsigned char *Word)
  1140. {
  1141.     unsigned char    *dp, *sp;
  1142.  
  1143.     for (dp = sp = Word; *sp != 0; sp++)
  1144.     {
  1145.     if (*sp != CHAR_MAGIC)
  1146.         *dp++ = *sp;
  1147.     }
  1148.  
  1149.     *dp = 0;
  1150.     return (char *)Word;
  1151. }
  1152.  
  1153. /*
  1154.  * tilde expansion
  1155.  *
  1156.  * based on a version by Arnold Robbins
  1157.  *
  1158.  * Think this needs Expanable strings!!
  1159.  */
  1160.  
  1161. static char * F_LOCAL TildeSubstitution (unsigned char *acp)
  1162. {
  1163.     unsigned        c;
  1164.     unsigned char    path[FFNAME_MAX];
  1165.     unsigned char    *cp = acp;
  1166.     unsigned char    *wp = path;
  1167.     unsigned char    *dp;
  1168.  
  1169.     while (TRUE)
  1170.     {
  1171.     while (TRUE)
  1172.     {
  1173.         if ((c = *cp++) == 0)
  1174.         {
  1175.         *wp = 0;
  1176.         ReleaseMemoryCell ((void *)acp);
  1177.         return StringCopy ((char *)path);
  1178.         }
  1179.  
  1180.         else if ((c == CHAR_MAGIC) && (*cp == CHAR_TILDE))
  1181.         break;
  1182.  
  1183.         else
  1184.         *wp++ = (char)c;
  1185.     }
  1186.  
  1187.     dp = NULL;    /* no output substitution */
  1188.  
  1189.  /*
  1190.   * ~ or ~/
  1191.   */
  1192.  
  1193.     if ((cp[1] == 0) || IsPathCharacter (cp[1]) || (IsDriveCharacter (cp[1])))
  1194.     {
  1195.         dp = (unsigned char *)GetVariableAsString (HomeVariableName, FALSE);
  1196.         cp += 1;
  1197.     }
  1198.  
  1199.     else if ((cp[1] == '+') && (IsPathCharacter (cp[2]) ||
  1200.                     IsDriveCharacter (cp[2]) || (cp[2] == 0)))
  1201.     {
  1202.         dp = (unsigned char *)GetVariableAsString (PWDVariable, FALSE);
  1203.         cp += 2;
  1204.     }
  1205.  
  1206.     else if ((cp[1] == '-') && (IsPathCharacter (cp[2]) ||
  1207.                     IsDriveCharacter (cp[2]) || (cp[2] == 0)))
  1208.     {
  1209.         dp = (unsigned char *)GetVariableAsString (OldPWDVariable, FALSE);
  1210.         cp += 2;
  1211.     }
  1212.  
  1213. /* substitute */
  1214.  
  1215.     if (dp != NULL)
  1216.     {
  1217.         while (*dp != 0)
  1218.         *wp++ = *dp++;
  1219.  
  1220. /* Remove double //'s on directories */
  1221.  
  1222.         if (IsPathCharacter (*(wp - 1)) && IsPathCharacter (*cp))
  1223.         cp++;
  1224.     }
  1225.     }
  1226. }
  1227.  
  1228. /*
  1229.  * Sort Compare
  1230.  */
  1231.  
  1232. int    SortCompare (const void *a1, const void *a2)
  1233. {
  1234.     return strcmp (*((char **)a1), *((char **)a2));
  1235. }
  1236.  
  1237. /*
  1238.  * Return the position of Prefix StopWord in the quoted string
  1239.  */
  1240.  
  1241. static char * F_LOCAL WordScan (char *QString, int StopWord)
  1242. {
  1243.     int        VarSubNest = 0;
  1244.  
  1245.     while (TRUE)
  1246.     {
  1247.     switch (*(QString++))
  1248.     {
  1249.         case WORD_EOS:
  1250.         return QString;
  1251.  
  1252.         case WORD_CHAR:
  1253.         case WORD_QCHAR:
  1254.         case WORD_QTCHAR:
  1255.         QString++;
  1256.         break;
  1257.  
  1258.         case WORD_OQUOTE:
  1259.         case WORD_ODQUOTE:
  1260.         case WORD_CQUOTE:
  1261.         case WORD_CDQUOTE:
  1262.         break;
  1263.  
  1264.         case WORD_OARRAY:
  1265.         VarSubNest++;
  1266.         break;
  1267.  
  1268.         case WORD_CARRAY:
  1269.         if ((StopWord == WORD_CARRAY) && (VarSubNest == 0))
  1270.             return QString;
  1271.  
  1272.         VarSubNest--;
  1273.         break;
  1274.  
  1275.         case WORD_OSUBST:
  1276.         VarSubNest++;
  1277.  
  1278.         while (*(QString++) != 0)
  1279.             continue;
  1280.  
  1281.         if (*QString != WORD_CSUBST)
  1282.             QString++;
  1283.  
  1284.         break;
  1285.  
  1286.         case WORD_CSUBST:
  1287.         if ((StopWord == WORD_CSUBST) && (VarSubNest == 0))
  1288.             return QString;
  1289.  
  1290.         VarSubNest--;
  1291.         break;
  1292.  
  1293.         case WORD_COMSUB:
  1294.         case WORD_OMATHS:
  1295.         while (*(QString++) != 0)
  1296.             continue;
  1297.  
  1298.         break;
  1299.     }
  1300.     }
  1301. }
  1302.  
  1303. /*
  1304.  * Maths substitute - convert $((....)) to a number.
  1305.  */
  1306.  
  1307. static int F_LOCAL MathsSubstitute (Expand *xp, char *sp)
  1308. {
  1309.     char    DecimalString[12];
  1310.     char    *esp;
  1311.  
  1312.     esp = substitute (sp, 0);
  1313.     sprintf (DecimalString, "%lu", EvaluateMathsExpression (esp));
  1314.     xp->str = StringCopy (DecimalString);
  1315.     return XSUB;
  1316. }
  1317.  
  1318.  
  1319. /*
  1320.  * Check for multi_drive prefix
  1321.  */
  1322.  
  1323. #if (OS_TYPE != OS_UNIX)
  1324. static unsigned char * F_LOCAL CheckForMultipleDrives (unsigned char *prefix)
  1325. {
  1326.     if ((*(prefix++) != CHAR_MAGIC) || (!IS_WildCard (*prefix)))
  1327.     return (unsigned char *)NULL;
  1328.  
  1329.     if (*prefix != CHAR_OPEN_BRACKETS)
  1330.     return *(prefix + 1) == CHAR_DRIVE ? prefix + 1 : (unsigned char *)NULL;
  1331.  
  1332.     while (*prefix && (*prefix != CHAR_CLOSE_BRACKETS))
  1333.     {
  1334.     if ((*prefix == CHAR_MATCH_RANGE) && (*(prefix + 1)))
  1335.         ++prefix;
  1336.  
  1337.     ++prefix;
  1338.     }
  1339.  
  1340.     return (*prefix && (*(prefix + 1) == CHAR_DRIVE))
  1341.         ? prefix + 1 : (unsigned char *)NULL;
  1342. }
  1343. #endif
  1344.  
  1345. /*
  1346.  * A command tree is to be expanded for stdin
  1347.  */
  1348.  
  1349. static bool F_LOCAL ProcessCommandTree (C_Op *outtree, int localpipe)
  1350. {
  1351.     long        s_flags = flags;
  1352.     Break_C        *S_RList = Return_List;    /* Save loval links    */
  1353.     Break_C        *S_BList = Break_List;
  1354.     Break_C        *S_SList = SShell_List;
  1355.     bool        s_ProcessingEXECCommand = ProcessingEXECCommand;
  1356.     int            Local_depth = Execute_stack_depth++;
  1357.     jmp_buf        ReturnPoint;
  1358.     int            ReturnValue;
  1359.     FunctionList    *s_CurrentFunction = CurrentFunction;
  1360.     Break_C        bc;
  1361.  
  1362. /* Create the pipe to read the output from the command string */
  1363.  
  1364.     S_dup2 (localpipe, 1);
  1365.  
  1366.     FL_CLEAR (FLAG_EXIT_ON_ERROR);
  1367.     FL_CLEAR (FLAG_ECHO_INPUT);
  1368.     FL_CLEAR (FLAG_NO_EXECUTE);
  1369.  
  1370. /* Set up new environment */
  1371.  
  1372.     ReturnValue = CreateGlobalVariableList (FLAGS_NONE);
  1373.  
  1374.     if ((ReturnValue != -1) && (!SetErrorPoint (ReturnPoint)))
  1375.     {
  1376.     Return_List = (Break_C *)NULL;
  1377.     Break_List  = (Break_C *)NULL;
  1378.  
  1379. /* Clear execute flags.  */
  1380.  
  1381.     ProcessingEXECCommand = TRUE;
  1382.     CurrentFunction = (FunctionList *)NULL;
  1383.  
  1384. /* Parse the line and execute it */
  1385.  
  1386.     if (setjmp (bc.CurrentReturnPoint) == 0)
  1387.     {
  1388.         bc.NextExitLevel = SShell_List;
  1389.         SShell_List = &bc;
  1390.         ReturnValue = ExecuteParseTree (outtree, NOPIPE, NOPIPE, 0);
  1391.     }
  1392.  
  1393. /* Parse error */
  1394.  
  1395.     else
  1396.         ReturnValue = -1;
  1397.  
  1398. /* Clean up any files around we nolonger need */
  1399.  
  1400.     ClearExtendedLineFile ();
  1401.     }
  1402.  
  1403.     else
  1404.     ReturnValue = -1;
  1405.  
  1406. /* Restore environment */
  1407.  
  1408.     RestoreEnvironment (ReturnValue, Local_depth);
  1409.  
  1410. /* Free old space */
  1411.  
  1412.     FreeAllHereDocuments (MemoryAreaLevel);
  1413.  
  1414. /* Ok - completed processing - restore environment and read the pipe */
  1415.  
  1416.     ProcessingEXECCommand = s_ProcessingEXECCommand;
  1417.     flags = s_flags;
  1418.     Return_List = S_RList;
  1419.     Break_List    = S_BList;
  1420.     SShell_List = S_SList;
  1421.     CurrentFunction = s_CurrentFunction;
  1422.  
  1423. /* Move pipe to start so we can read it */
  1424.  
  1425.     lseek (localpipe, 0L, SEEK_SET);
  1426.     return C2bool (ReturnValue != -1);
  1427. }
  1428.  
  1429. /*    (pc@hillside.co.uk)
  1430.  * I have decided to `fudge' alternations by picking up the compiled command
  1431.  * tree and working with it recursively to generate the set of arguments.
  1432.  * This has the advantage of making a single discrete change to the code
  1433.  *
  1434.  * This routine calls itself recursively
  1435.  *
  1436.  *    a) Scan forward looking for { building the output string if none found
  1437.  *       then call expand - and exit
  1438.  *    b) When { found, scan forward finding the end }
  1439.  *    c) Add first alternate to output string
  1440.  *    d) Scan for the end of the string copying into output
  1441.  *    e) Call routine with new string
  1442.  *
  1443.  * Major complication is quoting
  1444.  */
  1445.  
  1446. static void F_LOCAL AlternationExpand (char   *cp,    /* input word    */
  1447.                        Word_B **WordList,/* output words    */
  1448.                        int    ExpandMode)/* DO* flags    */
  1449. {
  1450.     char    *srcp = cp;
  1451.     char    *left;        /* destination string of left hand side    */
  1452.     char    *leftend;    /* end of left hand side        */
  1453.     char    *alt;        /* start of alterate section        */
  1454.     char    *altend;    /* end of alternate section        */
  1455.     char    *ap;        /* working pointer            */
  1456.     char    *right;        /* right hand side            */
  1457.     char    *rp;        /* used to copy right-hand side        */
  1458.     size_t    maxlen;        /* max string length            */
  1459.  
  1460.     maxlen  = WordScan (cp, 0) - cp;
  1461.     left    = GetAllocatedSpace (maxlen);
  1462.     leftend = left;
  1463.  
  1464.     if (AlternationScan (&srcp, &leftend, CHAR_OPEN_BRACES, 0) == 0)
  1465.     {
  1466.     ExpandAWord (cp, WordList, ExpandMode & EXPAND_NOALTS);
  1467.     ReleaseMemoryCell (left);
  1468.     return;
  1469.     }
  1470.  
  1471. /* We have a alternation section */
  1472.  
  1473.     alt = GetAllocatedSpace (maxlen);
  1474.     altend = alt;
  1475.     srcp += 2;
  1476.  
  1477.     if (AlternationScan (&srcp, &altend, CHAR_CLOSE_BRACES, 1) == 0)
  1478.     {
  1479.     ReleaseMemoryCell (left);
  1480.     ReleaseMemoryCell (alt);
  1481.     PrintErrorMessage ("Mis-matched {}.");
  1482.     }
  1483.  
  1484.     *(altend++) = WORD_CHAR;
  1485.     *(altend++) = ',';
  1486.     *altend = WORD_EOS;
  1487.  
  1488. /* finally we may have a right-hand side */
  1489.  
  1490.     right = srcp + 2;
  1491.  
  1492. /* glue the bits together making a new string */
  1493.  
  1494.     for (srcp = alt; *srcp != WORD_EOS;)
  1495.     {
  1496.     ap = leftend;
  1497.  
  1498.     if (AlternationScan (&srcp, &ap, ',', -1) == 0)
  1499.     {
  1500.         ReleaseMemoryCell (left);
  1501.         ReleaseMemoryCell (alt);
  1502.         PrintErrorMessage ("Missing comma.");
  1503.     }
  1504.  
  1505.     srcp += 2;
  1506.     rp = right;
  1507.     AlternationScan (&rp, &ap, WORD_EOS, 0);
  1508.     AlternationExpand (left, WordList, ExpandMode);
  1509.     }
  1510.  
  1511.     ReleaseMemoryCell (left);
  1512.     ReleaseMemoryCell (alt);
  1513.     return;
  1514. }
  1515.  
  1516. /*
  1517.  * Scan the tree
  1518.  */
  1519.  
  1520. static int F_LOCAL AlternationScan (char **cpp,/* source pointer      */
  1521.                         char **dpp,/* destination pointer */
  1522.                         char endc, /* last character look for  */
  1523.                         int  bal)
  1524. {
  1525.     char    *cp, *dp;
  1526.     bool    QuoteStatus = FALSE;
  1527.     int        balance = 0;
  1528.     bool    UseBalance = FALSE;
  1529.     int        VarSubNest = 0;
  1530.  
  1531.     if (bal)
  1532.     {
  1533.     UseBalance = TRUE;
  1534.     balance = (bal < 1) ? 0 : 1;
  1535.     }
  1536.  
  1537.     cp = *cpp;
  1538.     dp = *dpp;
  1539.  
  1540.     while (*cp != WORD_EOS)
  1541.     {
  1542.     switch (*cp)
  1543.     {
  1544.         case WORD_CHAR:
  1545.         if (QuoteStatus)
  1546.         {
  1547.             if (cp[1] == CHAR_CLOSE_BRACKETS)
  1548.             QuoteStatus = FALSE;
  1549.         }
  1550.  
  1551.         else if (!QuoteStatus)
  1552.         {
  1553.             if (cp[1] == CHAR_OPEN_BRACKETS)
  1554.             QuoteStatus = TRUE;
  1555.  
  1556.             else
  1557.             {
  1558.             if (UseBalance)
  1559.             {
  1560.                 if (cp[1] == CHAR_OPEN_BRACES)
  1561.                 balance++;
  1562.  
  1563.                 if (cp[1] == CHAR_CLOSE_BRACES)
  1564.                 balance--;
  1565.             }
  1566.  
  1567.             if ((cp[1] == endc) && (balance == 0))
  1568.             {
  1569.                 *dp = WORD_EOS;
  1570.                 *dpp = dp;
  1571.                 *cpp = cp;
  1572.                 return 1;
  1573.             }
  1574.             }
  1575.         }
  1576.  
  1577.         case WORD_QCHAR:
  1578.         case WORD_QTCHAR:
  1579.         *(dp++) = *(cp++);
  1580.         *(dp++) = *(cp++);
  1581.         break;
  1582.  
  1583.         case WORD_OQUOTE:
  1584.         case WORD_ODQUOTE:
  1585.         QuoteStatus = TRUE;
  1586.         *(dp++) = *(cp++);
  1587.         break;
  1588.  
  1589.         case WORD_CQUOTE:
  1590.         case WORD_CDQUOTE:
  1591.         QuoteStatus = FALSE;
  1592.         *(dp++) = *(cp++);
  1593.         break;
  1594.  
  1595.         case WORD_OARRAY:
  1596.         VarSubNest++;
  1597.         *(dp++) = *(cp++);
  1598.         break;
  1599.  
  1600.         case WORD_OSUBST:
  1601.         VarSubNest++;
  1602.  
  1603.         while ((*(dp++) = *(cp++)))
  1604.             continue;
  1605.  
  1606.         if (*cp != WORD_CSUBST)
  1607.             *(dp++) = *(cp++);
  1608.  
  1609.         break;
  1610.  
  1611.         case WORD_CSUBST:
  1612.         case WORD_CARRAY:
  1613.         *(dp++) = *(cp++);
  1614.         VarSubNest--;
  1615.         break;
  1616.  
  1617.         case WORD_COMSUB:
  1618.         case WORD_OMATHS:
  1619.         while ((*(dp++) = *(cp++)))
  1620.             continue;
  1621.  
  1622.         break;
  1623.     }
  1624.     }
  1625.  
  1626.     *dp = WORD_EOS;
  1627.     *cpp = cp;
  1628.     *dpp = dp;
  1629.  
  1630.     return 0;
  1631. }
  1632.  
  1633. /*
  1634.  * Handle Array Value between WORD_OARRAY & WORD_CARRAY
  1635.  * Return the array index (-1 == [*]).
  1636.  *              (-2 == [@]).
  1637.  */
  1638.  
  1639. static int F_LOCAL HandleArrayValue (char *name,
  1640.                      char **InputString,
  1641.                      int  ExpandMode)
  1642. {
  1643.                 /* Start after the Open Array        */
  1644.     char    *End = WordScan ((*InputString) + 1, WORD_CARRAY);
  1645.     size_t    Length = (End - *InputString) - 1;
  1646.     char    *ExpWord;
  1647.     Word_B    *WordList = (Word_B *)NULL;
  1648.     long    value;
  1649.  
  1650. /* Build a copy of the substring to expand */
  1651.  
  1652.     ExpWord = memcpy (GetAllocatedSpace (Length), *InputString + 1, Length);
  1653.     ExpWord[Length - 1] = WORD_EOS;
  1654.  
  1655.     ExpandMode &= ~(EXPAND_GLOBBING | EXPAND_CONVERT | EXPAND_NOALTS);
  1656.     ExpandAWord (ExpWord, &WordList, ExpandMode);
  1657.  
  1658. /* Check for valid value */
  1659.  
  1660.     if (WordBlockSize (WordList) != 1)
  1661.     ShellErrorMessage (LIT_BadArray, "too many words");
  1662.  
  1663. /*
  1664.  * There are a couple of special cases:
  1665.  *
  1666.  * ${#name[*]}
  1667.  * ${name[*]}
  1668.  * ${name[@]}
  1669.  */
  1670.  
  1671.     if (!strcmp (WordList->w_words[0], "*"))
  1672.     value = -1L;
  1673.  
  1674.     else if (!strcmp (WordList->w_words[0], "@") &&
  1675.          ((*name != '#') || (*(name + 1) == 0)))
  1676.     value = -2L;
  1677.  
  1678. /*
  1679.  * Otherwise, get the array value
  1680.  */
  1681.  
  1682.     else
  1683.     {
  1684.     value = EvaluateMathsExpression (WordList->w_words[0]);
  1685.  
  1686.     if ((value < 0) || (value > INT_MAX))
  1687.         ShellErrorMessage (LIT_ArrayRange, name);
  1688.     }
  1689.  
  1690.     *InputString = End;
  1691.     return (int)value;
  1692. }
  1693.  
  1694. /*
  1695.  * Check for Unset Variable
  1696.  */
  1697.  
  1698. static void F_LOCAL CheckForUnset (char *name, int Index)
  1699. {
  1700.     if ((FL_TEST (FLAG_UNSET_ERROR)) &&
  1701.     (GetVariableArrayAsString (name, Index, FALSE) == null))
  1702.     ShellErrorMessage ("%s: unset variable", name);
  1703. }
  1704.  
  1705.  
  1706. /*
  1707.  * TWALK - Build list of the values of an Environment Variable
  1708.  */
  1709.  
  1710. static void BuildVariableEntryList (const void *key, VISIT visit, int level)
  1711. {
  1712.     VariableList    *vp = (*(VariableList **)key);
  1713.  
  1714.     if (((visit == postorder) || (visit == leaf)) &&
  1715.        (strcmp (GVAV_Name, vp->name) == 0))
  1716.     GVAV_WordList = AddWordToBlock (GetVariableArrayAsString (vp->name,
  1717.                                   vp->index,
  1718.                                   TRUE),
  1719.                     GVAV_WordList);
  1720. }
  1721.  
  1722. /*
  1723.  * Return the number of floppy disks
  1724.  */
  1725.  
  1726. #if (OS_TYPE == OS_OS2)
  1727. static int F_LOCAL GetNumberofFloppyDrives (void)
  1728. {
  1729.     BYTE    nflop = 1;
  1730.  
  1731. #  if (OS_SIZE == OS_16)
  1732.     DosDevConfig (&nflop, DEVINFO_FLOPPY, 0);
  1733. #  else
  1734.     DosDevConfig (&nflop, DEVINFO_FLOPPY);
  1735. #  endif
  1736.  
  1737.     return nflop;
  1738. }
  1739. #endif
  1740.  
  1741. /* DOS Version */
  1742.  
  1743. #if (OS_TYPE == OS_DOS)
  1744. static int F_LOCAL GetNumberofFloppyDrives (void)
  1745. {
  1746. #  if defined (__TURBOC__)
  1747.  
  1748.     return ((biosequip () & 0x00c0) >> 6) + 1;
  1749.  
  1750. #  elif defined (__EMX__)
  1751.  
  1752.     union REGS        r;
  1753.  
  1754.     SystemInterrupt (0x11, &r, &r);
  1755.     return ((r.x.REG_AX & 0x00c0) >> 6) + 1;
  1756.  
  1757. #  else
  1758.  
  1759.     return ((_bios_equiplist () & 0x00c0) >> 6) + 1;
  1760.  
  1761. #  endif
  1762. }
  1763. #endif
  1764.  
  1765. /* NT Version */
  1766.  
  1767. #if (OS_TYPE == OS_NT)
  1768. static int F_LOCAL GetNumberofFloppyDrives (void)
  1769. {
  1770.     char    szNewDrive[4];
  1771.     DWORD    dwLogicalDrives = GetLogicalDrives();
  1772.     int        LastTest = 0;
  1773.     int        i;
  1774.  
  1775.     strcpy (szNewDrive, "x:\\");
  1776.  
  1777. /* Look at each drive until we find a non-floppy which exists */
  1778.  
  1779.     for (i = 0; i < 25; i++)
  1780.     {
  1781.     if (dwLogicalDrives & (1L << i))
  1782.     {
  1783.         szNewDrive[0] = i + 'A';
  1784.  
  1785.         if (GetDriveType (szNewDrive) != DRIVE_REMOVABLE)
  1786.         break;
  1787.  
  1788.         LastTest = i + 1;
  1789.     }
  1790.     }
  1791.  
  1792.     return LastTest;
  1793. }
  1794. #endif
  1795.